
#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"
#include "semphr.h"

#include "gkt_malloc.h"
#include "gkt_os.h"
#include "gkt_debug.h"

#if 0
#define OS_TIMEOUT_MS_2_TICKS(timeout)	\
	((timeout != GKT_INFINITE) ? ((timeout + portTICK_PERIOD_MS - 1) / portTICK_PERIOD_MS) : GKT_INFINITE)
#else
#include "gkt_systime.h"
static uint32_t OS_TIMEOUT_MS_2_TICKS(uint32_t timeout_ms)
{
	uint32_t systime_start_ms, systime_end_ms, ticks;

	if ((timeout_ms > 0) && (timeout_ms != GKT_INFINITE)) {
		systime_start_ms = gkt_systime_ms();
		systime_end_ms = systime_start_ms + timeout_ms + portTICK_PERIOD_MS - 1;
		ticks = (systime_end_ms / portTICK_PERIOD_MS) - (systime_start_ms / portTICK_PERIOD_MS);
	}
	else if (0 == timeout_ms)
		ticks = 0;
	else
		ticks = GKT_INFINITE;

	return ticks;
}
#endif
gkt_thread_t gkt_thread_create(
				gkt_thread_routine_fn routine, 
				void *arguments, 
				gkt_thread_priority_e priority,
				uint32_t stack_size, 
				gkt_thread_extend_attr_s *extend_attr)
{
	TaskHandle_t task;
	char empty_name;
	const char *thread_name;
	uint16_t stack_depth;
	BaseType_t x_Return;

	GKT_ASSERT(!gkt_irq_context());

	if (routine 
		&& (priority < GKT_THREAD_PRIORITY_MAX)) 
	{
		/* thread name */
		if (extend_attr && extend_attr->name)
			thread_name = extend_attr->name;
		else {
			empty_name = '\0';
			thread_name = &empty_name;
		}


		/* stack depth */
		if (stack_size < GKT_CONFIG_OS_THREAD_STACK_MINSIZE)
			stack_depth = GKT_CONFIG_OS_THREAD_STACK_MINSIZE >> 2;
		else
			stack_depth = (uint16_t)(stack_size >> 2);

		x_Return = xTaskCreate((TaskFunction_t)routine, 
						thread_name, stack_depth, arguments, 
						priority, &task);
		if (x_Return != pdPASS)
			task = NULL;
	}

	return (gkt_thread_t)task;
}

int gkt_thread_destroy(gkt_thread_t thread)
{
	eTaskState task_state;
	int retval;

	GKT_ASSERT(!gkt_irq_context());

	if (thread) {
		task_state = eTaskGetState ((TaskHandle_t)thread);
		if (task_state != eDeleted) {
			vTaskDelete ((TaskHandle_t)thread);
			retval = GKT_SUCCESS;
		}
		else
			retval = GKT_EPERMIT;
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

gkt_thread_t gkt_thread_current(void)
{
	GKT_ASSERT(!gkt_irq_context());
	return (gkt_thread_t)xTaskGetCurrentTaskHandle();
}

static gkt_thread_state_e sc_thread_states[eInvalid] = {
	GKT_THREAD_STATE_RUNNING,	/* eRunning */
	GKT_THREAD_STATE_READY,		/* eReady */
	GKT_THREAD_STATE_BLOCKED,	/* eBlocked */
	GKT_THREAD_STATE_BLOCKED,	/* eSuspended */
	GKT_THREAD_STATE_TERMINATED,	/* eDeleted */
};
gkt_thread_state_e gkt_thread_get_state(gkt_thread_t thread)
{
	eTaskState task_state;
	gkt_thread_state_e state = GKT_THREAD_STATE_ERROR;

	GKT_ASSERT(!gkt_irq_context());

	if (thread) {
		task_state = eTaskGetState ((TaskHandle_t)thread);
		if (task_state < eInvalid)
			state = sc_thread_states[task_state];
	}

	return state;
} 

int gkt_thread_get_priority(gkt_thread_t thread)
{
	GKT_ASSERT(!gkt_irq_context());
	return thread ? (int)uxTaskPriorityGet ((TaskHandle_t)thread) : -1;
}

int gkt_thread_set_priority(gkt_thread_t thread, 
			gkt_thread_priority_e priority)
{
	int retval;

	if (thread 
		&& (priority < GKT_THREAD_PRIORITY_MAX)) 
	{
		vTaskPrioritySet ((TaskHandle_t)thread, (UBaseType_t)priority);
		retval = GKT_SUCCESS;
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

void gkt_thread_yield(void)
{
	GKT_ASSERT(!gkt_irq_context());
	taskYIELD();
}

int gkt_thread_suspend(gkt_thread_t thread)
{
	int retval;

	if (thread) {
		vTaskSuspend((TaskHandle_t)thread);
		retval = GKT_SUCCESS;
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

int gkt_thread_resume(gkt_thread_t thread)
{
	int retval;

	if (thread) {
		vTaskResume((TaskHandle_t)thread);
		retval = GKT_SUCCESS;
	}
	else
		retval = GKT_EPARAM;
		
	return retval;
}

__NO_RETURN void gkt_thread_exit(uint32_t retval)
{
	vTaskDelete (NULL);
	while(1);
}

void gkt_thread_sleep(uint32_t time_ms)
{
	uint32_t ticks;

	GKT_ASSERT(!gkt_irq_context());
	if (time_ms) {
		ticks = OS_TIMEOUT_MS_2_TICKS(time_ms);
		vTaskDelay(ticks);
	}
}

/* mutex */
gkt_mutex_t gkt_mutex_create(uint32_t attr)
{
	gkt_mutex_t mutex;

	GKT_ASSERT(!gkt_irq_context());

#if GKT_CONFIG_OS_MUTEX_RECURSIVE
	if (attr & GKT_MUTEX_RECURSIVE) {
		mutex = xSemaphoreCreateRecursiveMutex();
		if (mutex)
			mutex = (gkt_mutex_t)((uint32_t)mutex | 1U);
	}
	else
#endif
		mutex = (gkt_mutex_t)xSemaphoreCreateMutex();

	return mutex;
}

void gkt_mutex_destroy(gkt_mutex_t mutex)
{
	SemaphoreHandle_t mutex_handle;

	GKT_ASSERT(!gkt_irq_context());

	if (mutex) {
#if GKT_CONFIG_OS_MUTEX_RECURSIVE
		mutex_handle = (SemaphoreHandle_t)((uint32_t)mutex & (~1U));
#else
		mutex_handle = (SemaphoreHandle_t)mutex;
#endif
		vSemaphoreDelete(mutex_handle);
	}
}

int gkt_mutex_lock(gkt_mutex_t mutex, uint32_t timeout)
{
	SemaphoreHandle_t mutex_handle;
	TickType_t ticks;
	BaseType_t x_Return;
	int retval;

	GKT_ASSERT(!gkt_irq_context());

	if (mutex) {
		ticks = (TickType_t)OS_TIMEOUT_MS_2_TICKS(timeout);
#if GKT_CONFIG_OS_MUTEX_RECURSIVE
		uint32_t mutex_recursive = (uint32_t)mutex & 1U;
		mutex_handle = (SemaphoreHandle_t)((uint32_t)mutex & (~1U));
		if (mutex_recursive)
			x_Return = xSemaphoreTakeRecursive(mutex_handle, ticks);
		else
#else
		mutex_handle = (SemaphoreHandle_t)mutex;
#endif
			x_Return = xSemaphoreTake(mutex_handle, ticks);

		if (pdPASS == x_Return)
			retval = GKT_SUCCESS;
		else if (ticks)
			retval = GKT_ETIMEOUT;
		else
			retval = GKT_ENORESOURCE;
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

int gkt_mutex_unlock(gkt_mutex_t mutex)
{
	SemaphoreHandle_t mutex_handle;
	BaseType_t x_Return;
	int retval;

	GKT_ASSERT(!gkt_irq_context());

	if (mutex) {
#if GKT_CONFIG_OS_MUTEX_RECURSIVE
		uint32_t mutex_recursive = (uint32_t)mutex & 1U;
		mutex_handle = (SemaphoreHandle_t)((uint32_t)mutex & (~1U));
		if (mutex_recursive)
			x_Return = xSemaphoreGiveRecursive(mutex_handle);
		else
#else
		mutex_handle = (SemaphoreHandle_t)mutex;
#endif
			x_Return = xSemaphoreGive(mutex_handle);

		if (pdPASS == x_Return)
			retval = GKT_SUCCESS;
		else
			retval = GKT_EPERMIT;	/* unlocked, could not release again */
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

gkt_thread_t gkt_mutex_get_owner(gkt_mutex_t mutex)
{
	SemaphoreHandle_t mutex_handle;
	gkt_thread_t owner;

	GKT_ASSERT(!gkt_irq_context());

	if (mutex) {
#if GKT_CONFIG_OS_MUTEX_RECURSIVE
		mutex_handle = (SemaphoreHandle_t)((uint32_t)mutex & (~1U));
#else
		mutex_handle = (SemaphoreHandle_t)mutex;
#endif
		owner = (gkt_thread_t)xSemaphoreGetMutexHolder(mutex_handle);
	}
	else
		owner = NULL;

	return owner;
}

gkt_semaphore_t gkt_semaphore_create(uint32_t max_count, 
						uint32_t initial_count)
{
	SemaphoreHandle_t sem;
	BaseType_t x_return;

	GKT_ASSERT(!gkt_irq_context());
	if ((max_count > 0) 
		&& (initial_count <= max_count))
	{
		if (max_count > 1)
			sem = xSemaphoreCreateCounting(max_count, initial_count);
		else {
			sem = xSemaphoreCreateBinary();
			if (sem && initial_count) {
				x_return = xSemaphoreGive(sem);
				if (x_return != pdPASS) {
					vSemaphoreDelete(sem);
					sem = NULL;
				}
			}
		}
	}
	else
		sem = NULL;

	return (gkt_semaphore_t)sem;
}

void gkt_semaphore_destroy(gkt_semaphore_t sem)
{
	GKT_ASSERT(!gkt_irq_context());
	if (sem)
		vSemaphoreDelete((SemaphoreHandle_t)sem);
}

int gkt_semaphore_acquire(gkt_semaphore_t sem, 
						uint32_t timeout)
{
	BaseType_t x_return;
	int retval;

	GKT_ASSERT(!gkt_irq_context());

	if (sem) {
		x_return = xSemaphoreTake((SemaphoreHandle_t)sem, 
						(TickType_t)OS_TIMEOUT_MS_2_TICKS(timeout));
		if (pdPASS == x_return)
			retval = GKT_SUCCESS;
		else if (timeout)
			retval = GKT_ETIMEOUT;
		else
			retval = GKT_ENORESOURCE;
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

int gkt_semaphore_release(gkt_semaphore_t sem)
{
	BaseType_t x_return;
	int retval;

	if (sem) {
		if (gkt_irq_context())
			x_return = xSemaphoreGiveFromISR((SemaphoreHandle_t)sem, NULL);
		else
			x_return = xSemaphoreGive((SemaphoreHandle_t)sem);

		if (pdPASS == x_return)
			retval = GKT_SUCCESS;
		else
			retval = GKT_ENORESOURCE;
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

int gkt_semaphore_get_count(gkt_semaphore_t sem)
{
	int count;

	if (sem) {
		if (gkt_irq_context())
			count = (int)uxQueueMessagesWaitingFromISR ((QueueHandle_t)sem);
		else
			count = (int)uxSemaphoreGetCount ((SemaphoreHandle_t)sem);
	}
	else
		count = -1;

	return count;
}

/*
 * mwssage queue(mqueue)
 */
gkt_mqueue_t gkt_mqueue_create(uint32_t msg_count, 
					uint32_t msg_size)
{
	gkt_mqueue_t mqueue;

	GKT_ASSERT(!gkt_irq_context());

	if (msg_count && msg_size)
		mqueue = (gkt_mqueue_t *)xQueueCreate(msg_count, msg_size);
	else
		mqueue = NULL;

	return mqueue;
}

void gkt_mqueue_destroy(gkt_mqueue_t mqueue)
{
	GKT_ASSERT(!gkt_irq_context());

	if (mqueue)
		vQueueDelete ((QueueHandle_t)mqueue);
}

int gkt_mqueue_put(gkt_mqueue_t mqueue, 
			const void *message, uint32_t timeout)
{
	BaseType_t x_Return; 
	int retval;

	if (mqueue && message) {
		if (gkt_irq_context()) {
			if (timeout)
				retval = GKT_EPARAM;
			else {
				x_Return = xQueueSendToBackFromISR ((QueueHandle_t)mqueue, message, NULL);
				retval = (pdTRUE == x_Return) ? GKT_SUCCESS : GKT_ENORESOURCE;
			}	
		}
		else {
			x_Return = xQueueSendToBack((QueueHandle_t)mqueue, message, 
							(TickType_t)OS_TIMEOUT_MS_2_TICKS(timeout));
			if (pdPASS == x_Return)
				retval = GKT_SUCCESS;
			else if (timeout)
				retval = GKT_ETIMEOUT;
			else
				retval = GKT_ENORESOURCE;
		}
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

int gkt_mqueue_get(gkt_mqueue_t mqueue, 
			void *message, uint32_t timeout)
{
	BaseType_t x_Return; 
	int retval;

	GKT_ASSERT(!gkt_irq_context());

	if (mqueue && message) {
		x_Return = xQueueReceive((QueueHandle_t)mqueue, message, 
						(TickType_t)OS_TIMEOUT_MS_2_TICKS(timeout));
		if (pdPASS == x_Return)
			retval = GKT_SUCCESS;
		else if (timeout)
			retval = GKT_ETIMEOUT;
		else
			retval = GKT_ENORESOURCE;
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

int gkt_mqueue_get_count(gkt_mqueue_t mqueue)
{
	int count;

	if (mqueue) {
		if (gkt_irq_context())
			count = (int)uxQueueMessagesWaitingFromISR ((QueueHandle_t)mqueue);
		else
			count = (int)uxQueueMessagesWaiting ((QueueHandle_t)mqueue);
	}
	else
		count = -1;

	return count;
}

/* soft timer */
typedef struct _soft_timer_item {
	gkt_soft_timer_callback_fn	callback;
	void	*arguments;
} soft_timer_item_s;

static void soft_timer_callback(TimerHandle_t timer)
{
	soft_timer_item_s *timer_item;

	timer_item = (soft_timer_item_s *)pvTimerGetTimerID (timer);
	if (timer_item)
		(*timer_item->callback)(timer_item->arguments);
}

gkt_soft_timer_t gkt_soft_timer_create(
						gkt_soft_timer_type_e type,
						gkt_soft_timer_callback_fn callback,
						void *arguments)
{
	gkt_soft_timer_t timer = NULL;
	soft_timer_item_s *timer_item;
	UBaseType_t auto_reload;

	GKT_ASSERT(!gkt_irq_context());

	if (callback) {
		timer_item = gkt_malloc(sizeof(soft_timer_item_s));
		if (timer_item) {
			timer_item->callback = callback;
			timer_item->arguments = arguments;
		
			auto_reload = (GKT_SOFT_TIMER_PERIODIC == type) ? pdTRUE : pdFALSE;

			timer = (gkt_soft_timer_t)xTimerCreate(NULL, 1, auto_reload, 
											timer_item, soft_timer_callback);
		}
	}

	return timer;
}

int gkt_soft_timer_destroy(gkt_soft_timer_t timer)
{
	soft_timer_item_s *timer_item;
	BaseType_t x_Return;
	int retval;

	GKT_ASSERT(!gkt_irq_context());

	if (timer) {
		timer_item = (soft_timer_item_s *)pvTimerGetTimerID ((TimerHandle_t)timer);
		x_Return = xTimerDelete ((TimerHandle_t)timer, 0);
		if (pdPASS == x_Return) {
			gkt_free(timer_item);
			retval = GKT_SUCCESS;
		}
		else
			retval = GKT_ELOWLEVEL;
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

int gkt_soft_timer_start(gkt_soft_timer_t timer,
			uint32_t interval_ms)
{
	TickType_t ticks;
	BaseType_t x_Return;
	int retval;

	GKT_ASSERT(!gkt_irq_context());

	if (timer && (interval_ms > 0)) {
		ticks = (TickType_t)OS_TIMEOUT_MS_2_TICKS(interval_ms);
		x_Return = xTimerChangePeriod((TimerHandle_t)timer, ticks, 0);
		retval = (pdPASS == x_Return) ? GKT_SUCCESS : GKT_ELOWLEVEL;
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

int gkt_soft_timer_stop(gkt_soft_timer_t timer)
{
	BaseType_t x_Return;
	int retval;

	GKT_ASSERT(!gkt_irq_context());

	if (timer) {
		x_Return = xTimerIsTimerActive((TimerHandle_t)timer);
		if (pdTRUE == x_Return) {
			x_Return = xTimerStop ((TimerHandle_t)timer, 0);
			retval = (pdPASS == x_Return) ? GKT_SUCCESS : GKT_ELOWLEVEL;
		}
		else
			retval = GKT_EREPETITIVE;
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

/* Heap region definition used by heap_5 variant */
#if defined(GKT_CONFIG_OS_HEAP_TYPE) && (5 == GKT_CONFIG_OS_HEAP_TYPE)
#if (configAPPLICATION_ALLOCATED_HEAP == 1)
/*
 * The application writer has already defined the array used for the RTOS
 * heap - probably so it can be placed in a special segment or address.
 */
extern uint8_t ucHeap[];
#elif (configTOTAL_HEAP_SIZE > 0)
static uint8_t ucHeap[configTOTAL_HEAP_SIZE];
#else
#error "configTOTAL_HEAP_SIZE should > 0"
#endif /* configAPPLICATION_ALLOCATED_HEAP */

static HeapRegion_t xHeapRegions[] = {
  { ucHeap, configTOTAL_HEAP_SIZE },
  { NULL,   0                     }
};
#endif /* GKT_CONFIG_OS_HEAP_TYPE == 5 */

int gkt_kernel_init(void)
{
#if defined(GKT_CONFIG_OS_HEAP_TYPE) && (5 == GKT_CONFIG_OS_HEAP_TYPE)
	vPortDefineHeapRegions(xHeapRegions);
#endif

	return GKT_SUCCESS;
}

#ifdef GKT_CONFIG_MAIN_TASK_PRIORITY
#define MAIN_TASK_PRIORITY	GKT_THREAD_PRIORITY_BY_NAME(GKT_CONFIG_MAIN_TASK_PRIORITY)
#else
#define MAIN_TASK_PRIORITY	GKT_THREAD_PRIORITY_NORMAL
#endif

#ifdef GKT_CONFIG_MAIN_TASK_STACKSIZE
#define MAIN_TASK_STACKSIZE	GKT_CONFIG_MAIN_TASK_STACKSIZE
#else
#define MAIN_TASK_STACKSIZE	0x400
#endif

extern int gkt_main(void);
static void os_main_task_routine(void *params)
{
	int retval;
	gkt_trace("os_main_task_routine\n");
	retval = gkt_main();
	gkt_printf("main_task_routine: exit (%d)\n", retval);
	gkt_thread_exit(retval);
}

void gkt_kernel_start(void)
{
	gkt_thread_t main_thread;

	GKT_ASSERT(!gkt_irq_context());

	main_thread = gkt_thread_create(os_main_task_routine,
						NULL,
						MAIN_TASK_PRIORITY, 
						MAIN_TASK_STACKSIZE,
						NULL);
	if (main_thread)
		vTaskStartScheduler();
}

/*---------------------------------------------------------------------------*/

#if configSUPPORT_STATIC_ALLOCATION
/* Idle task control block and stack */
static StaticTask_t Idle_TCB;
static StackType_t  Idle_Stack[configMINIMAL_STACK_SIZE];

/* Timer task control block and stack */
static StaticTask_t Timer_TCB;
static StackType_t  Timer_Stack[configTIMER_TASK_STACK_DEPTH];

/*
  vApplicationGetIdleTaskMemory gets called when configSUPPORT_STATIC_ALLOCATION
  equals to 1 and is required for static memory allocation support.
*/
void vApplicationGetIdleTaskMemory (StaticTask_t **ppxIdleTaskTCBBuffer, 
			StackType_t **ppxIdleTaskStackBuffer, 
			uint32_t *pulIdleTaskStackSize) 
{
  *ppxIdleTaskTCBBuffer   = &Idle_TCB;
  *ppxIdleTaskStackBuffer = &Idle_Stack[0];
  *pulIdleTaskStackSize   = (uint32_t)configMINIMAL_STACK_SIZE;
}

/*
  vApplicationGetTimerTaskMemory gets called when configSUPPORT_STATIC_ALLOCATION
  equals to 1 and is required for static memory allocation support.
*/
void vApplicationGetTimerTaskMemory (StaticTask_t **ppxTimerTaskTCBBuffer, 
			StackType_t **ppxTimerTaskStackBuffer, 
			uint32_t *pulTimerTaskStackSize) 
{
  *ppxTimerTaskTCBBuffer   = &Timer_TCB;
  *ppxTimerTaskStackBuffer = &Timer_Stack[0];
  *pulTimerTaskStackSize   = (uint32_t)configTIMER_TASK_STACK_DEPTH;
}
#endif

#if (configUSE_IDLE_HOOK == 1)
__WEAK void vApplicationIdleHook (void)
{

}
#endif

#if (configUSE_TICK_HOOK == 1)
__WEAK void vApplicationTickHook (void)
{

}
#endif

#if (configUSE_MALLOC_FAILED_HOOK == 1)
__WEAK void vApplicationMallocFailedHook (void)
{

}
#endif

#if (configCHECK_FOR_STACK_OVERFLOW > 0)
__WEAK void vApplicationStackOverflowHook (TaskHandle_t xTask, signed char *pcTaskName)
{
	gkt_printf("========== TASK STACK OVERFLOW ==========\n");
	gkt_printf("xTask = %p - %s\n", xTask, pcTaskName);
	__BKPT(0);
}
#endif

